﻿using Dapper;
using MyDapperApi.Repositories.Interfaces;
using System.Data;
using System.Linq.Expressions;
using MySql.Data.MySqlClient;

namespace MyDapperApi.Repositories.Implementations;

public abstract class RepositoryBase<T> : IRepository<T> where T : class
{
    protected readonly IConfiguration _configuration;
    protected readonly string _tableName;
    protected readonly string _connectionString;

    protected RepositoryBase(IConfiguration configuration, string tableName)
    {
        _configuration = configuration;
        _tableName = tableName;
        _connectionString = _configuration.GetConnectionString("DefaultConnection");
    }

    protected IDbConnection CreateConnection() => new MySqlConnection(_connectionString);

    // 获取主键名称（简单实现，实际中可能需要更复杂的逻辑）
    protected virtual string GetPrimaryKeyName() => "Id";

    public async Task<T?> GetByIdAsync(int id)
    {
        using var connection = CreateConnection();
        var keyName = GetPrimaryKeyName();
        var sql = $"SELECT * FROM {_tableName} WHERE {keyName} = @Id";
        return await connection.QueryFirstOrDefaultAsync<T>(sql, new { Id = id });
    }

    public async Task<T?> GetByIdAsync(string id)
    {
        using var connection = CreateConnection();
        var keyName = GetPrimaryKeyName();
        var sql = $"SELECT * FROM {_tableName} WHERE {keyName} = @Id";
        return await connection.QueryFirstOrDefaultAsync<T>(sql, new { Id = id });
    }

    public async Task<IEnumerable<T>> GetAllAsync()
    {
        using var connection = CreateConnection();
        var sql = $"SELECT * FROM {_tableName}";
        return await connection.QueryAsync<T>(sql);
    }

    // 注意：Dapper 不支持直接使用 Expression，这里需要手动构建查询
    // 这里提供一个简单的实现，实际项目中可能需要更复杂的查询构建器
    public async Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate)
    {
        // 简化实现：在实际项目中，你可能需要使用 LINQ to SQL 或其他查询构建器
        // 这里先获取所有数据，然后在内存中过滤（不推荐用于大数据集）
        var allItems = await GetAllAsync();
        return allItems.AsQueryable().Where(predicate).ToList();
    }

    public async Task<T?> FirstOrDefaultAsync(Expression<Func<T, bool>> predicate)
    {
        var items = await FindAsync(predicate);
        return items.FirstOrDefault();
    }

    public async Task<int> AddAsync(T entity)
    {
        using var connection = CreateConnection();
        var properties = typeof(T).GetProperties().Where(p => p.Name != GetPrimaryKeyName());
        var columnNames = string.Join(", ", properties.Select(p => p.Name));
        var parameterNames = string.Join(", ", properties.Select(p => "@" + p.Name));

        var sql = $"INSERT INTO {_tableName} ({columnNames}) VALUES ({parameterNames})";
        return await connection.ExecuteAsync(sql, entity);
    }

    public async Task<int> UpdateAsync(T entity)
    {
        using var connection = CreateConnection();
        var properties = typeof(T).GetProperties().Where(p => p.Name != GetPrimaryKeyName());
        var setClause = string.Join(", ", properties.Select(p => $"{p.Name} = @{p.Name}"));
        var keyName = GetPrimaryKeyName();

        var sql = $"UPDATE {_tableName} SET {setClause} WHERE {keyName} = @{keyName}";
        return await connection.ExecuteAsync(sql, entity);
    }

    public async Task<int> DeleteAsync(int id)
    {
        using var connection = CreateConnection();
        var keyName = GetPrimaryKeyName();
        var sql = $"DELETE FROM {_tableName} WHERE {keyName} = @Id";
        return await connection.ExecuteAsync(sql, new { Id = id });
    }

    public async Task<int> DeleteAsync(string id)
    {
        using var connection = CreateConnection();
        var keyName = GetPrimaryKeyName();
        var sql = $"DELETE FROM {_tableName} WHERE {keyName} = @Id";
        return await connection.ExecuteAsync(sql, new { Id = id });
    }

    public async Task<int> CountAsync(Expression<Func<T, bool>> predicate = null)
    {
        using var connection = CreateConnection();
        var sql = $"SELECT COUNT(*) FROM {_tableName}";

        if (predicate != null)
        {
            // 简化实现
            var allItems = await GetAllAsync();
            return allItems.AsQueryable().Count(predicate);
        }

        return await connection.ExecuteScalarAsync<int>(sql);
    }

    public async Task<bool> ExistsAsync(Expression<Func<T, bool>> predicate)
    {
        return (await CountAsync(predicate)) > 0;
    }
}